#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "../src/network/connection_pool.h"
#include "../src/utils/logger.h"
#include "../include/gudb.h"

// Test data structure
typedef struct {
    connection_pool_t* pool;
    uint32_t thread_id;
    uint32_t num_acquisitions;
    uint32_t successful_acquisitions;
    bool completed;
} test_thread_data_t;

// Mock database for testing
gudb* create_mock_database() {
    gudb* db = calloc(1, sizeof(gudb));
    if (db) {
        strcpy(db->data_dir, "test_db");
        // Initialize basic members for testing
        db->data_fd = -1;
        db->wal_fd = -1;
        db->binlog_fd = -1;
        db->meta_fd = -1;
        db->log_fd = -1;
        db->lock_fd = -1;
    }
    return db;
}

void destroy_mock_database(gudb* db) {
    if (db) {
        free(db);
    }
}

void test_connection_pool_creation() {
    printf("Testing connection pool creation...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_connection_pool.log");
    logger_config.enable_console_output = true;
    
    logger_t* logger = logger_create(&logger_config);
    assert(logger != NULL);
    logger_init(logger);
    logger_set_global(logger);
    
    // Create mock database
    gudb* db = create_mock_database();
    assert(db != NULL);
    
    // Test default configuration
    connection_pool_config_t config = connection_pool_default_config();
    assert(config.max_connections == 10000);
    assert(config.min_connections == 100);
    assert(config.initial_connections == 200);
    
    // Create connection pool
    connection_pool_t* pool = connection_pool_create(&config, db);
    assert(pool != NULL);
    
    // Initialize pool
    int result = connection_pool_init(pool);
    assert(result == 0);
    
    // Check initial state
    assert(connection_pool_get_free_count(pool) == config.initial_connections);
    assert(connection_pool_get_active_count(pool) == 0);
    assert(connection_pool_is_healthy(pool) == true);
    
    // Get statistics
    connection_pool_stats_t stats;
    result = connection_pool_get_stats(pool, &stats);
    assert(result == 0);
    
    printf("Pool statistics: idle=%llu, active=%llu, expected_idle=%u\n",
           stats.current_idle_connections, stats.current_active_connections, 
           config.initial_connections);
    
    // The statistics may not be updated until we call update_stats
    connection_pool_update_stats(pool);
    connection_pool_get_stats(pool, &stats);
    
    assert(stats.current_idle_connections >= 0);  // Just check it's reasonable
    assert(stats.current_active_connections == 0);
    
    // Cleanup
    connection_pool_destroy(pool);
    destroy_mock_database(db);
    logger_destroy(logger);
    
    printf("Connection pool creation test passed!\n\n");
}

void test_connection_acquisition_release() {
    printf("Testing connection acquisition and release...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_connection_pool.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    
    // Create smaller pool for testing
    connection_pool_config_t config = connection_pool_default_config();
    config.max_connections = 10;
    config.min_connections = 2;
    config.initial_connections = 5;
    
    connection_pool_t* pool = connection_pool_create(&config, db);
    connection_pool_init(pool);
    
    // Test acquiring connections
    pool_connection_t* conns[10];
    uint32_t acquired = 0;
    
    // Acquire all initial connections
    for (uint32_t i = 0; i < config.initial_connections; i++) {
        conns[i] = connection_pool_acquire(pool, 1); // 1 second timeout
        if (conns[i]) {
            acquired++;
            assert(conns[i]->pool_state == POOL_CONN_STATE_ACTIVE);
        }
    }
    
    assert(acquired == config.initial_connections);
    assert(connection_pool_get_active_count(pool) == acquired);
    assert(connection_pool_get_free_count(pool) == 0);
    
    // Try to acquire more (should create new connections)
    for (uint32_t i = config.initial_connections; i < config.max_connections; i++) {
        conns[i] = connection_pool_acquire(pool, 1);
        if (conns[i]) {
            acquired++;
        }
    }
    
    assert(acquired <= config.max_connections);
    
    // Release half of the connections
    uint32_t to_release = acquired / 2;
    for (uint32_t i = 0; i < to_release; i++) {
        if (conns[i]) {
            int result = connection_pool_release(pool, conns[i]);
            assert(result == 0);
            conns[i] = NULL;
        }
    }
    
    // Check that connections are back in free list
    assert(connection_pool_get_free_count(pool) >= to_release);
    assert(connection_pool_get_active_count(pool) == acquired - to_release);
    
    // Release remaining connections
    for (uint32_t i = to_release; i < config.max_connections; i++) {
        if (conns[i]) {
            connection_pool_release(pool, conns[i]);
            conns[i] = NULL;
        }
    }
    
    // Print final statistics
    connection_pool_print_stats(pool);
    
    connection_pool_destroy(pool);
    destroy_mock_database(db);
    logger_destroy(logger);
    
    printf("Connection acquisition and release test passed!\n\n");
}

// Thread function for concurrent testing
void* test_thread_func(void* arg) {
    test_thread_data_t* data = (test_thread_data_t*)arg;
    
    for (uint32_t i = 0; i < data->num_acquisitions; i++) {
        // Acquire connection
        pool_connection_t* conn = connection_pool_acquire(data->pool, 2);
        if (conn) {
            data->successful_acquisitions++;
            
            // Simulate some work
            usleep(1000 + (rand() % 5000)); // 1-6ms
            
            // Release connection
            connection_pool_release(data->pool, conn);
        }
        
        // Small delay between acquisitions
        usleep(100 + (rand() % 900)); // 0.1-1ms
    }
    
    data->completed = true;
    return NULL;
}

void test_concurrent_access() {
    printf("Testing concurrent connection pool access...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_connection_pool.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    
    // Create moderate-sized pool
    connection_pool_config_t config = connection_pool_default_config();
    config.max_connections = 50;
    config.min_connections = 10;
    config.initial_connections = 20;
    
    connection_pool_t* pool = connection_pool_create(&config, db);
    connection_pool_init(pool);
    
    // Start background threads
    connection_pool_start_background_threads(pool);
    
    // Create multiple test threads
    const uint32_t num_threads = 8;
    const uint32_t acquisitions_per_thread = 100;
    
    pthread_t threads[num_threads];
    test_thread_data_t thread_data[num_threads];
    
    // Initialize thread data
    for (uint32_t i = 0; i < num_threads; i++) {
        thread_data[i].pool = pool;
        thread_data[i].thread_id = i;
        thread_data[i].num_acquisitions = acquisitions_per_thread;
        thread_data[i].successful_acquisitions = 0;
        thread_data[i].completed = false;
    }
    
    // Start threads
    for (uint32_t i = 0; i < num_threads; i++) {
        pthread_create(&threads[i], NULL, test_thread_func, &thread_data[i]);
    }
    
    // Wait for threads to complete
    for (uint32_t i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
        assert(thread_data[i].completed == true);
        printf("Thread %u: %u/%u successful acquisitions\n", 
               i, thread_data[i].successful_acquisitions, 
               thread_data[i].num_acquisitions);
    }
    
    // Check final statistics
    connection_pool_stats_t stats;
    connection_pool_get_stats(pool, &stats);
    
    uint32_t total_successful = 0;
    for (uint32_t i = 0; i < num_threads; i++) {
        total_successful += thread_data[i].successful_acquisitions;
    }
    
    printf("Total successful acquisitions: %u\n", total_successful);
    printf("Pool statistics:\n");
    printf("  Total requests: %llu\n", stats.connection_requests);
    printf("  Pool hits: %llu\n", stats.connection_pool_hits);
    printf("  Pool misses: %llu\n", stats.connection_pool_misses);
    printf("  Timeouts: %llu\n", stats.connection_wait_timeouts);
    printf("  Peak connections: %llu\n", stats.peak_connections);
    
    // Verify pool is still healthy
    assert(connection_pool_is_healthy(pool) == true);
    
    connection_pool_destroy(pool);
    destroy_mock_database(db);
    logger_destroy(logger);
    
    printf("Concurrent access test passed!\n\n");
}

void test_connection_timeout_and_cleanup() {
    printf("Testing connection timeout and cleanup...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_connection_pool.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    
    // Create pool with short timeouts for testing
    connection_pool_config_t config = connection_pool_default_config();
    config.max_connections = 20;
    config.min_connections = 5;
    config.initial_connections = 10;
    config.connection_timeout_sec = 2;  // 2 second timeout
    config.cleanup_interval_sec = 1;    // Clean up every second
    
    connection_pool_t* pool = connection_pool_create(&config, db);
    connection_pool_init(pool);
    
    // Acquire some connections
    pool_connection_t* conns[5];
    for (int i = 0; i < 5; i++) {
        conns[i] = connection_pool_acquire(pool, 1);
        assert(conns[i] != NULL);
    }
    
    // Release them
    for (int i = 0; i < 5; i++) {
        connection_pool_release(pool, conns[i]);
    }
    
    uint32_t free_before = connection_pool_get_free_count(pool);
    printf("Free connections before timeout: %u\n", free_before);
    
    // Wait for connections to timeout
    sleep(3);
    
    // Manually trigger cleanup
    int cleaned = connection_pool_cleanup_idle_connections(pool);
    printf("Cleaned up %d idle connections\n", cleaned);
    
    uint32_t free_after = connection_pool_get_free_count(pool);
    printf("Free connections after cleanup: %u\n", free_after);
    
    // Should still have minimum connections
    assert(free_after >= config.min_connections);
    
    connection_pool_destroy(pool);
    destroy_mock_database(db);
    logger_destroy(logger);
    
    printf("Connection timeout and cleanup test passed!\n\n");
}

void test_pool_statistics() {
    printf("Testing pool statistics...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_connection_pool.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    
    connection_pool_config_t config = connection_pool_default_config();
    config.max_connections = 10;
    config.initial_connections = 5;
    
    connection_pool_t* pool = connection_pool_create(&config, db);
    connection_pool_init(pool);
    
    // Get initial statistics
    connection_pool_stats_t stats_before;
    connection_pool_get_stats(pool, &stats_before);
    
    // Perform some operations
    pool_connection_t* conn1 = connection_pool_acquire(pool, 1);
    pool_connection_t* conn2 = connection_pool_acquire(pool, 1);
    
    connection_pool_release(pool, conn1);
    connection_pool_release(pool, conn2);
    
    // Update and get statistics
    connection_pool_update_stats(pool);  
    connection_pool_stats_t stats_after;
    connection_pool_get_stats(pool, &stats_after);
    
    // Verify statistics were updated
    assert(stats_after.connection_requests > stats_before.connection_requests);
    assert(stats_after.connection_pool_hits > stats_before.connection_pool_hits);
    
    // Print comprehensive statistics
    connection_pool_print_stats(pool);
    
    connection_pool_destroy(pool);
    destroy_mock_database(db);
    logger_destroy(logger);
    
    printf("Pool statistics test passed!\n\n");
}

int main() {
    printf("=== GuDB Connection Pool Manager Test ===\n\n");
    
    // Initialize random seed
    srand(time(NULL));
    
    test_connection_pool_creation();
    test_connection_acquisition_release();
    test_concurrent_access();
    test_connection_timeout_and_cleanup();
    test_pool_statistics();
    
    printf("=== All connection pool tests completed successfully! ===\n");
    printf("Check the generated log file: test_connection_pool.log\n");
    
    return 0;
}